package jkws

import (
	"encoding/json"
	cbase "findService/ss/servicebase"
	"golang.org/x/net/websocket"
	"jkgo/jk/jklog"
	"jkgo/jk/utils"
	"jkgo/jkws"
)

type wsHandler struct {
	handler  *jkws.CMWSHandler
	iot      *wsIoTHandler
	msglist  *CMWSMsgList
	recvData string
}

var seq uint

// NewWSHandler new websocket handler
func newWSHandler(addr string, port int) (*wsHandler, error) {
	jklog.L().Infof("New websocket server [%s:%d]\n", addr, port)
	ws := &wsHandler{}
	var err error
	ws.handler, err = jkws.NewCMWSHandler(addr, port)
	if err != nil {
		jklog.L().Errorln(err)
		return nil, err
	}
	ws.handler.Add("/jkws", ws.handlerWSServer)
	go ws.handler.Start()

	ws.msglist = newWSMsgList()

	return ws, nil
}

func newWSHandlerTls(addr string, port int, cert, key string) (*wsHandler, error) {
	jklog.L().Infof("New websocket server tls [%s:%d]\n", addr, port)
	ws := &wsHandler{}
	var err error
	ws.handler, err = jkws.NewCMWSHandler(addr, port)
	if err != nil {
		jklog.L().Errorln(err)
		return nil, err
	}
	ws.handler.Add("/jkws", ws.handlerWSServer)
	go ws.handler.StartTls(cert, key)

	ws.msglist = newWSMsgList()

	return ws, nil
}

func (ws *wsHandler) setIoT(iot *wsIoTHandler) {
	ws.iot = iot
}

func (ws *wsHandler) dealCommand(wc *websocket.Conn, mcsrc *cbase.MsgPayload) error {
	jklog.L().Debugln("ws deal command of ", mcsrc.ID, " with conn ", wc)

	var err error

	// create new for mqtt transfer
	mc := cbase.MsgPayload{}
	seq = mc.Make("jkws", mcsrc.Cmd, seq, mcsrc.ToType, mcsrc.Data)

	str, _ := json.Marshal(&mc)

	ws.iot.IoTPub(imgprocName+"/"+mcsrc.Cmd, string(str))

	msg := CMWSMsg{
		conn:    wc,
		src:     *mcsrc,
		payload: mc,
	}
	ws.msglist.Add(&msg)

	return err
}

func (ws *wsHandler) makeResponse(topic, msg string) error {
	jklog.L().Debugln("ws response topic ", topic, ", msg ", msg)

	if topic == imgprocResponse {
		var mc cbase.MsgPayload
		err := json.Unmarshal([]byte(msg), &mc)
		if err != nil {
			jklog.L().Errorln(err)
		} else {
			out := ws.msglist.Find(mc.Seq)
			if out != nil {
				jklog.L().Debugln("ws send response ", mc, " with conn ", out.conn)
				out.src.Data = mc.Data
				out.src.Retime()
				sdata, err := json.Marshal(out.src)
				if err != nil {
					ws.send(out.conn, "Error response")
				} else {
					ws.send(out.conn, string(sdata))
				}
			} else {
				jklog.L().Infoln("not find this message topic", topic)
			}
		}
	} else {

	}

	return nil
}

func (ws *wsHandler) send(wc *websocket.Conn, data string) error {
	_, err := wc.Write([]byte(data))
	if err != nil {
		jklog.L().Errorln(err)
		return err
	}
	return nil
}

func (ws *wsHandler) handlerWSServer(wc *websocket.Conn) {
	jklog.L().Debugln("ws one client coming ", wc.RemoteAddr().String())

	for {
		msglen := make([]byte, 4)
		jklog.L().Infoln(msglen)
		_, err := wc.Read(msglen)
		if err != nil {
			jklog.L().Infoln("ws recv done")
			break
		} else {
			lendata := int(utils.BytesToInt32(msglen))
			jklog.L().Infoln("need read length of ", lendata)
			if lendata <= 0 || lendata > (2<<28) {
				continue
			}
			msgd := make([]byte, lendata)
			outlen := 0
			for {
				if outlen >= lendata {
					break
				}
				n, err := wc.Read(msgd[outlen:])
				if err != nil {
					jklog.L().Errorln("read error ", err)
					continue
				}
				outlen += n
				jklog.L().Debugln("len ", n, ", ", outlen)
			}
			if outlen == lendata {
				mc := &cbase.MsgPayload{}
				err := json.Unmarshal(msgd[:outlen], mc)
				if err != nil {
					jklog.L().Errorln("error messsage ", err)
					continue
				}
				jklog.L().Infoln("deal of cmd ", mc.Cmd)
				ws.dealCommand(wc, mc)
			} else {
				jklog.L().Errorf("wrong message len [%d] need [%d]\n", outlen, lendata)
			}
		}
	}
}
